/*
 * main.c
 *
 *  Created on: 06/06/2012
 *      Author: Roy
 */

#include "dbl_list.h"
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include "barrier.h"

extern list dbl_List;
int  finished;
pthread_mutex_t mutex;
pthread_mutex_t out_l;
pthread_cond_t is_finished;
barrier_t* bar;
List pids;

typedef struct params_t{
	int key;
	char val;

}*params;

//***********************************AUX functions*********************************************
void* aux_begin(void* p){
	pthread_t temp=pthread_self();
	listInsertFirst(pids,(ListElement)&temp);

	finished=1;
	pthread_cond_signal(&is_finished);

	barrier(bar);
	printf("BEGIN");
	pthread_mutex_unlock(&out_l);
	return NULL;
}


void* aux_insert_head(void* p){
	int k=((params)p)->key;
	int v=((params)p)->val;
	bool result=true;
	pthread_t temp=pthread_self();
	listInsertFirst(pids,(ListElement)&temp);
	finished=1;
	pthread_cond_signal(&is_finished);

	barrier(bar);

	//func
	//result=InsertHead(k,v);

	//printing
	pthread_mutex_lock(&out_l);
	printf("INSERT_HEAD %d %c->%s",k, v, (result?"TRUE":"FALSE") );
	pthread_mutex_unlock(&out_l);

	return NULL;
}

void* aux_insert_tail(void* p){
	int k=((params)p)->key;
	int v=((params)p)->val;
	bool result=true;
	pthread_t temp=pthread_self();
	listInsertFirst(pids,(ListElement)&temp);
	finished=1;
	pthread_cond_signal(&is_finished);

	barrier(bar);

	//func
	//result=InsertTail(k,v);

	//printing
	pthread_mutex_lock(&out_l);
	printf("INSERT_TAIL %d %c->%s",k, v, (result?"TRUE":"FALSE") );
	pthread_mutex_unlock(&out_l);

	return NULL;
}


void* aux_search(void* p){
	int k=((params)p)->key;
	char c;
	bool result=true;
	pthread_t temp=pthread_self();
	listInsertFirst(pids,(ListElement)&temp);
	finished=1;
	pthread_cond_signal(&is_finished);

	barrier(bar);

	//func
	//result=Search(k,&c);

	//printing
	pthread_mutex_lock(&out_l);
	printf("SEARCH %d->%s",k, (result?"TRUE":"FALSE") );
	pthread_mutex_unlock(&out_l);

	return NULL;
}

void* aux_delete(void* p){
	int k=((params)p)->key;
	bool result = true;
	pthread_t temp=pthread_self();
	listInsertFirst(pids,(ListElement)&temp);
	finished=1;
	pthread_cond_signal(&is_finished);

	barrier(bar);

	//func
	//result=Delete(k);

	//printing
	pthread_mutex_lock(&out_l);
	printf("DELETE %d->%s",k, (result?"TRUE":"FALSE") );
	pthread_mutex_unlock(&out_l);

	return NULL;
}

//***************************************************************************************************
ListElement copyPthread(ListElement n){
	pthread_t* result=(pthread_t*)malloc(sizeof(pthread_t));
	*result=*((pthread_t*)n);
	return (ListElement) result;
}

void deletePthread(ListElement n){
	free(n);
}

void exe_command(void){
	params par;
	char buf[35];
	int key;
	char val;
	char command[20];
	void** dumtemp;


	pids=listCreate(copyPthread, deletePthread);
	bar->arrived=0;
	while(1){

		fgets(buf, 35, stdin);
		sscanf(buf,"%s %d %c", command, &key, &val);

		// wait until last thread finished copying the params
		pthread_mutex_lock(&mutex);
		while(finished!=1){
			pthread_cond_wait(&is_finished, &mutex);
		}
		pthread_mutex_unlock(&mutex);

		par->key=key;
		par->val=val;

		if(strcmp(command,"BEGIN")==0){
			Initialize();
			pthread_mutex_lock(&out_l);
			finished=0;
			pthread_create(NULL,  NULL, aux_begin, NULL);
		}

		else if((strcmp(command,"INSERT_HEAD")==0)){
			finished=0;
			pthread_create(NULL , NULL, aux_insert_head,(void*)par);
		}
		else if((strcmp(command,"INSERT_TAIL")==0)){
			finished=0;
			pthread_create(NULL, NULL, aux_insert_tail,(void*)par);
		}
		else if((strcmp(command,"DELETE")==0)){
			finished=0;
			pthread_create(NULL, NULL, aux_delete,(void*)par);
		}
		else if((strcmp(command,"SEARCH")==0)){
			finished=0;
			pthread_create(NULL, NULL, aux_search,(void*)par);
		}
		else if((strcmp(command,"BARRIER")==0)){
		bar->arrived=1;
		pthread_cond_broadcast(&bar->reachedNumOfThreads);

		// now all threads will do their tasks
		while(listGetSize(pids)!=0){
			pthread_join(*(pthread_t*)listGetFirst(pids), dumtemp);
			listRemoveCurrent(pids);
		}
		// clear deleted nodes
		listClear(dbl_List->zombies);

		printf("BARRIER\n");
		bar->arrived=0;
		}
		else if((strcmp(command,"END")==0)){
			bar->arrived=1;
			pthread_cond_broadcast(&bar->reachedNumOfThreads);

			// now all threads will do their tasks
			while(listGetSize(pids)!=0){
				pthread_join(*(pthread_t*)listGetFirst(pids), dumtemp);
				listRemoveCurrent(pids);
			}
			// clear deleted nodes
			listClear(dbl_List->zombies);
			printf("END\n");
			break;
			}
	}

}



int main(){
	pthread_t main_thread;
	finished=1;
	pthread_mutexattr_t attr;

	assert(barrier_init(bar)==0);
	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
	pthread_mutex_init(&mutex, &attr);
	pthread_mutexattr_destroy(&attr);

	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
	pthread_mutex_init(&out_l, &attr);
	pthread_mutexattr_destroy(&attr);

	pthread_cond_init(&is_finished, NULL);

	exe_command();

	listDestroy(pids);
	Destroy();
	pthread_mutex_destroy(&mutex);
	pthread_mutex_destroy(&out_l);
	pthread_cond_destroy(&is_finished);
	barrier_destroy(bar);



	return 0;
}
